package kp.rpc.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.hash.Hash;
import kp.rpc.RpcApplication;
import kp.rpc.config.RpcConfig;
import kp.rpc.constants.RpcConstant;
import kp.rpc.fault.retry.RetryStrategy;
import kp.rpc.fault.retry.factory.RetryStrategyFactory;
import kp.rpc.fault.tolerant.TolerantStrategy;
import kp.rpc.fault.tolerant.factory.TolerantStrategyFactory;
import kp.rpc.loadbalancer.LoadBalancer;
import kp.rpc.loadbalancer.factory.LoadBalancerFactory;
import kp.rpc.model.RpcRequest;
import kp.rpc.model.RpcResponse;
import kp.rpc.model.ServiceMetaInfo;
import kp.rpc.registry.Registry;
import kp.rpc.registry.factory.RegistryFactory;
import kp.rpc.serializer.Serializer;
import kp.rpc.serializer.factory.SerializerFactory;
import kp.rpc.server.client.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

/**
 * 服务代理（JDK 动态代理）
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 动态获取序列化器
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        // 构造请求
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .serviceVersion(rpcConfig.getVersion())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        List<ServiceMetaInfo> failTolerantList = null;
        ServiceMetaInfo failServiceMetaInfo = null;
        try {
            //拿到注册中心
            Registry registry = RegistryFactory.getRegistry(rpcConfig.getRegistryConfig().getRegisterType());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo().setServiceName(rpcRequest.getServiceName())
                    .setServiceVersion(rpcRequest.getServiceVersion());
            // 根据服务名获取服务信息
            List<ServiceMetaInfo> serviceMetaInfoCollection = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            failTolerantList = serviceMetaInfoCollection;
            failServiceMetaInfo = serviceMetaInfo;
            if (CollUtil.isEmpty(serviceMetaInfoCollection)) {
                throw new RuntimeException("未发现对应的服务");
            }
            LoadBalancer loadBalancer = LoadBalancerFactory.getLoadBalancer(rpcConfig.getLoadBalancer());
            //构造loadBalancer请求参数
            HashMap<String, Object> requestParams = new HashMap<>();
            requestParams.put("method", method.getName());
            //选取服务
            serviceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoCollection);
            // 发送tcp请求
            //获取重试器
            RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
            ServiceMetaInfo finalServiceMetaInfo = serviceMetaInfo;
            RpcResponse rpcResponse = retryStrategy.doRetry(() -> VertxTcpClient.doRpcResponse(rpcRequest, finalServiceMetaInfo));
            return rpcResponse.getData();
        } catch (Exception e) {
            //容错处理
            TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
            HashMap<String, Object> context = new HashMap<>();
            context.put("rpcRequest", rpcRequest);
            context.put("failTolerantList", failTolerantList);
            context.put("failServiceMetaInfo", failServiceMetaInfo);
            return tolerantStrategy.doTolerant(context, e);
        }
    }
}